use crate::error::{CompilerError, Result};
use crate::llvm_backend::LLVMBackend;
use inkwell::{values::BasicValueEnum, AddressSpace, IntPredicate};

impl<'ctx> LLVMBackend<'ctx> {
    /// Generate string.len() method call
    /// Returns the number of Unicode characters (runes) in the string
    ///
    /// This counts UTF-8 characters, not bytes.
    /// For example, "你好" returns 2, not 6
    pub(in crate::llvm_backend) fn generate_string_len_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the string pointer
        let string_ptr = if let Some(var_value) = self.variable_map.get(var_name) {
            var_value.into_pointer_value()
        } else {
            return Err(CompilerError::internal(&format!(
                "Undefined variable: {}",
                var_name
            )));
        };

        // Load the string pointer value (i8*)
        let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
        let string_value = self
            .builder
            .build_load(i8_ptr_type, string_ptr, "string_val")?;
        let string_ptr_value = string_value.into_pointer_value();

        // Generate inline UTF-8 character counting
        // Algorithm:
        // - Count bytes that are NOT continuation bytes (10xxxxxx in binary, 0x80-0xBF)
        // - UTF-8 continuation bytes start with bits 10xxxxxx (0x80-0xBF)
        // - Other bytes are either ASCII or start bytes of multi-byte sequences

        let i8_type = self.context.i8_type();
        let i64_type = self.context.i64_type();

        // Create basic blocks
        let current_func = self.current_function.unwrap();
        let loop_cond_block = self
            .context
            .append_basic_block(current_func, "len_loop_cond");
        let loop_body_block = self
            .context
            .append_basic_block(current_func, "len_loop_body");
        let loop_end_block = self
            .context
            .append_basic_block(current_func, "len_loop_end");

        // Initialize counter and index
        let count_alloca = self.builder.build_alloca(i64_type, "rune_count")?;
        let index_alloca = self.builder.build_alloca(i64_type, "byte_index")?;

        self.builder
            .build_store(count_alloca, i64_type.const_int(0, false))?;
        self.builder
            .build_store(index_alloca, i64_type.const_int(0, false))?;

        // Jump to loop condition
        self.builder.build_unconditional_branch(loop_cond_block)?;

        // Loop condition: check if current byte is not null
        self.builder.position_at_end(loop_cond_block);
        let index_val = self
            .builder
            .build_load(i64_type, index_alloca, "index")?
            .into_int_value();

        // Get pointer to current byte: ptr + index
        let current_byte_ptr = unsafe {
            self.builder
                .build_gep(i8_type, string_ptr_value, &[index_val], "current_byte_ptr")?
        };
        let current_byte = self
            .builder
            .build_load(i8_type, current_byte_ptr, "current_byte")?
            .into_int_value();

        // Check if current byte is '\0' (end of string)
        let is_not_null = self.builder.build_int_compare(
            IntPredicate::NE,
            current_byte,
            i8_type.const_int(0, false),
            "is_not_null",
        )?;

        self.builder
            .build_conditional_branch(is_not_null, loop_body_block, loop_end_block)?;

        // Loop body: check if byte is NOT a UTF-8 continuation byte
        self.builder.position_at_end(loop_body_block);
        let index_val = self
            .builder
            .build_load(i64_type, index_alloca, "index")?
            .into_int_value();
        let current_byte_ptr = unsafe {
            self.builder
                .build_gep(i8_type, string_ptr_value, &[index_val], "current_byte_ptr")?
        };
        let current_byte = self
            .builder
            .build_load(i8_type, current_byte_ptr, "current_byte")?
            .into_int_value();

        // Check if byte is NOT a continuation byte (continuation bytes: 0x80-0xBF, i.e., 10xxxxxx)
        // A continuation byte has the form: (byte & 0xC0) == 0x80
        let masked_byte = self.builder.build_and(
            current_byte,
            i8_type.const_int(0xC0, false), // 11000000 in binary
            "masked_byte",
        )?;
        let is_continuation = self.builder.build_int_compare(
            IntPredicate::EQ,
            masked_byte,
            i8_type.const_int(0x80, false), // 10000000 in binary
            "is_continuation",
        )?;
        let is_not_continuation = self
            .builder
            .build_not(is_continuation, "is_not_continuation")?;

        // If NOT a continuation byte, increment count
        let current_count = self
            .builder
            .build_load(i64_type, count_alloca, "count")?
            .into_int_value();
        let is_not_cont_i64 =
            self.builder
                .build_int_z_extend(is_not_continuation, i64_type, "is_not_cont_i64")?;
        let new_count = self
            .builder
            .build_int_add(current_count, is_not_cont_i64, "new_count")?;
        self.builder.build_store(count_alloca, new_count)?;

        // Increment index
        let next_index =
            self.builder
                .build_int_add(index_val, i64_type.const_int(1, false), "next_index")?;
        self.builder.build_store(index_alloca, next_index)?;

        // Loop back to condition
        self.builder.build_unconditional_branch(loop_cond_block)?;

        // Loop end: return the count
        self.builder.position_at_end(loop_end_block);
        let final_count = self
            .builder
            .build_load(i64_type, count_alloca, "final_count")?;

        Ok(final_count)
    }


    /// Generate string.bytes_len() method call
    /// Returns the byte length of the string (UTF-8 encoded)
    ///
    /// This is equivalent to strlen().
    /// For example, "你好" returns 6 (3 bytes per character)
    pub(in crate::llvm_backend) fn generate_string_bytes_len_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the string pointer
        let string_ptr = if let Some(var_value) = self.variable_map.get(var_name) {
            var_value.into_pointer_value()
        } else {
            return Err(CompilerError::internal(&format!(
                "Undefined variable: {}",
                var_name
            )));
        };

        // Load the string pointer value (i8*)
        let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
        let string_value = self
            .builder
            .build_load(i8_ptr_type, string_ptr, "string_val")?;
        let string_ptr_value = string_value.into_pointer_value();

        // Call strlen to get byte length
        let strlen_func = if let Some(func) = self.function_map.get("strlen") {
            *func
        } else {
            return Err(CompilerError::internal("strlen function not found"));
        };

        // Call strlen
        let result =
            self.builder
                .build_call(strlen_func, &[string_ptr_value.into()], "bytes_len")?;

        // strlen returns i32, we need to extend it to i64 for consistency
        let i32_result = result.try_as_basic_value().left().unwrap().into_int_value();
        let i64_result = self.builder.build_int_z_extend(
            i32_result,
            self.context.i64_type(),
            "bytes_len_i64",
        )?;

        Ok(i64_result.into())
    }


    /// Generate string.is_empty() method call
    /// Returns true if the string is empty (length is 0)
    pub(in crate::llvm_backend) fn generate_string_is_empty_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the string pointer
        let string_ptr = if let Some(var_value) = self.variable_map.get(var_name) {
            var_value.into_pointer_value()
        } else {
            return Err(CompilerError::internal(&format!(
                "Undefined variable: {}",
                var_name
            )));
        };

        // Load the string pointer value (i8*)
        let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
        let string_value = self
            .builder
            .build_load(i8_ptr_type, string_ptr, "string_val")?;
        let string_ptr_value = string_value.into_pointer_value();

        // Load the first byte of the string
        let i8_type = self.context.i8_type();
        let first_byte = self
            .builder
            .build_load(i8_type, string_ptr_value, "first_byte")?;
        let first_byte_int = first_byte.into_int_value();

        // Check if first byte is '\0' (empty string)
        let is_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            first_byte_int,
            i8_type.const_int(0, false),
            "is_empty",
        )?;

        Ok(is_empty.into())
    }
}
